﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using JiveMessenger.Models;
using JiveMessenger.User;
using JmShared;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Command;
using System.Windows;
using System;
using JiveMessenger.Converters;
using System.Diagnostics;
using JiveMessenger.AvalonEdit;
using System.Windows.Media;
using JiveMessenger.Utilities;
using NLog;
using System.Collections.Specialized;
using System.IO;
using System.Windows.Media.Imaging;


namespace JiveMessenger.ViewModels
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class ViewModelContactList : ViewModelBase, IViewModelContactList
    {
        public enum MessengerCommands
        {
            RefreshContactsFilter,
            UpdateFontSize,
            UpdateCustomEmoticons,
            /// <summary>
            /// string username
            /// </summary>
            SetContactAsOffline,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetContactFromUsername,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetContactFromUserID,
            /// <summary>
            /// returns IContact
            /// </summary>
            GetCurrentUserContact,
            /// <summary>
            /// returns collection of IContact
            /// </summary>
            GetContacts,
            /// <summary>
            /// ClientStatus manual client status
            /// </summary>
            UpdateManualClientStatus,

        }
        private bool _isConnected;
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private IClient _currentClient;
        private readonly object LockObject = new object();
        public RelayCommand<IContact> AddContactCommand { get; set; }
        public RelayCommand OpenAddContactWindowCommand { get; set; }
        public RelayCommand OpenCreateGroupWindowCommand { get; set; }
        public RelayCommand<object> InviteContactToGroupCommand { get; set; }
        public RelayCommand<object> LeaveGroupCommand { get; set; }
        public RelayCommand<object> BlockGroupCommand { get; set; }
        public RelayCommand<IContact> OpenPmCommand { get; set; }
        public RelayCommand OpenNotificationsCommand { get; set; }
        public RelayCommand<IContact> IgnoreContactCommand { get; set; }
        public RelayCommand<IChatGroup> OpenGroupCommand { get; set; }
        public RelayCommand<IChatGroup> ManageGroupCommand { get; set; }
        public RelayCommand<IChatGroup> SetGroupExpandedCommand { get; set; }
        public RelayCommand<IChatGroup> SetGroupCollapsedCommand { get; set; }

        private FilteredObservableCollection<IContact> _contacts;
        public FilteredObservableCollection<IContact> Contacts
        {
            get { return _contacts; }
            set
            {
                if (_contacts != value)
                {
                    if (_contacts != null)
                    {
                        foreach (IContact contact in _contacts)
                            contact.PropertyChanged -= IContact_PropertyChanged;
                        _contacts.CollectionChanged -= Contacts_CollectionChanged;
                    }
                    Set(() => Contacts, ref _contacts, value);
                    if (_contacts != null)
                        _contacts.CollectionChanged += Contacts_CollectionChanged;
                }
            }
        }

        private CollectionViewSource _contactsViewSource;
        public CollectionViewSource ContactsViewSource
        {
            get { return _contactsViewSource; }
            set
            {
                if (_contactsViewSource != value)
                {
                    Set(() => ContactsViewSource, ref _contactsViewSource, value);
                }
            }
        }


        private IContact _currentUser;
        public IContact CurrentUser
        {
            get { return _currentUser; }
            set
            {
                if (_currentUser != value)
                {
                    if (value != null && value is JiveContactModel)
                    {
                        ((JiveContactModel)value).PropertyChanged -= CurrentUser_PropertyChanged;
                    }
                    Set(() => CurrentUser, ref _currentUser, value);
                    if (value != null && value is JiveContactModel)
                    {
                        ((JiveContactModel)value).PropertyChanged += CurrentUser_PropertyChanged;
                    }
                }
            }
        }

        private ClientStatus _manualClientStatus;
        public ClientStatus ManualClientStatus
        {
            get { return _manualClientStatus; }
            set
            {
                if (_manualClientStatus != value)
                {
                    Set(() => ManualClientStatus, ref _manualClientStatus, value);
                }
            }
        }



        /// <summary>
        /// Initializes a new instance of the ViewModelContactList class.
        /// </summary>
        public ViewModelContactList(IClient currentClient)
        {
            this._currentClient = currentClient;
            this.Contacts = new FilteredObservableCollection<IContact>();
            this.OpenAddContactWindowCommand = new RelayCommand(OpenAddContactWindowCommandAction);
            this.AddContactCommand = new RelayCommand<IContact>(AddContactCommandAction, CanExecuteAddContact);
            this.OpenCreateGroupWindowCommand = new RelayCommand(OpenCreateGroupWindowCommandAction);
            this.InviteContactToGroupCommand = new RelayCommand<object>(InviteContactToGroupCommandAction, CanExecuteAsGroupAdmin);
            this.LeaveGroupCommand = new RelayCommand<object>(LeaveGroupCommandAction, CanExecuteLeaveGroup);
            this.BlockGroupCommand = new RelayCommand<object>(BlockGroupCommandAction, CanExecuteBlockGroup);
            this.OpenPmCommand = new RelayCommand<IContact>(OpenPmCommandAction);
            this.OpenNotificationsCommand = new RelayCommand(OpenNotificationsCommandAction);
            this.IgnoreContactCommand = new RelayCommand<IContact>(IgnoreContactCommandAction);
            this.OpenGroupCommand = new RelayCommand<IChatGroup>(OpenGroupCommandAction);
            this.ManageGroupCommand = new RelayCommand<IChatGroup>(ManageGroupCommandAction, CanExecuteAsGroupAdmin);
            this.SetGroupExpandedCommand = new RelayCommand<IChatGroup>(SetGroupExpandedCommandAction);
            this.SetGroupCollapsedCommand = new RelayCommand<IChatGroup>(SetGroupCollapsedCommandAction);
            this.PropertyChanged += ViewModelContactList_PropertyChanged;

            Messenger.Default.Register<NotificationMessage>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "RefreshContactsFilter":
                            foreach (var contact in Contacts)
                            {
                                Contacts.RefreshFilter(contact);
                            }
                            break;
                        case "UpdateCustomEmoticons":
                            if (this.CurrentUser is JiveContactModel)
                            {
                                ((JiveContactModel)this.CurrentUser).CustomEmoticons = CreateCustomEmoticonList(this.CurrentUser, _currentClient.UserSettings.CustomEmoticons);
                            }
                            break;
                        case "UpdateFontSize":
                            {
                                this.CurrentUser.FontSize = _currentClient.UserSettings.FontSize.GetValueOrDefault(12);
                            }
                            break;
                    }
                });

            Messenger.Default.Register<NotificationMessage<string>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "SetContactAsOffline":
                            {
                                SetContactAsOffline(msg.Content);
                            }
                            break;
                    }
                });
            Messenger.Default.Register<NotificationMessageAction<IEnumerable<IContact>>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "GetContacts":
                        msg.Execute(Contacts);
                        break;
                }
            });
            Messenger.Default.Register<NotificationMessageAction<IContact>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "GetContactFromUsername":
                            {
                                IContact contact = null;
                                if (this.Contacts != null)
                                    contact = this.Contacts.FirstOrDefault(x => x.UserName == msg.Target.ToString() && x.ShowInContactList);
                                msg.Execute(contact);
                                break;
                            }
                        case "GetContactFromUserID":
                            {
                                IContact contact = null;
                                if (this.Contacts != null)
                                    contact = this.Contacts.FirstOrDefault(x => x.UserID == msg.Target.ToString() && x.ShowInContactList);
                                msg.Execute(contact);
                                break;
                            }
                        case "GetCurrentUserContact":
                            {
                                msg.Execute(this.CurrentUser);
                                break;
                            }
                    }
                });

        }

        private void OpenAddContactWindowCommandAction()
        {
            Messenger.Default.Send(new NotificationMessage(this, null, "OpenAddContacts"));
        }

        private void SetGroupCollapsedCommandAction(IChatGroup group)
        {
            if (_currentClient.IsLoggedIn && !group.IsExpanded)
            {
                _currentClient.UserSettings.ExpandedGroups.Remove(group.ID);
                Logger.Debug("Marked {0} as collapsed in user settings", group.ID);
            }
        }

        private void SetGroupExpandedCommandAction(IChatGroup group)
        {
            if (!_currentClient.UserSettings.ExpandedGroups.Contains(group.ID) && _currentClient.IsLoggedIn && group.IsExpanded)
            {
                _currentClient.UserSettings.ExpandedGroups.Add(group.ID);
                Logger.Debug("Marked {0} as expanded in user settings", group.ID);
            }
        }

        void ViewModelContactList_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ManualClientStatus":
                    if (_isConnected)
                        _currentClient.ChangeManualClientStatus(this.ManualClientStatus);
                    break;
            }
        }


        private void CurrentUser_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CustomEmoticons")
            {
                JiveContactModel contact = (JiveContactModel)sender;
                if (contact.CustomEmoticons != null)
                {
                    contact.CustomEmoticons.CollectionChanged -= CustomEmoticons_CollectionChanged;
                    contact.CustomEmoticons.CollectionChanged += CustomEmoticons_CollectionChanged;
                }
            }
        }
        void CustomEmoticons_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var v in e.NewItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce == null)
                        {
                            this._currentClient.UserSettings.CustomEmoticons.Add(new ContactEmoticon() { Keyword = m.EmoticonShortcut, Extension = Path.GetExtension(m.EmoticonPath) });
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var v in e.OldItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce != null)
                            this._currentClient.UserSettings.CustomEmoticons.Remove(ce);
                    }
                    break;
                case NotifyCollectionChangedAction.Replace:
                    foreach (var v in e.OldItems)
                    {
                        JiveCustomEmoticonModel m = (JiveCustomEmoticonModel)v;
                        ContactEmoticon ce = this._currentClient.UserSettings.CustomEmoticons.FirstOrDefault(x => x.Keyword == m.EmoticonShortcut);
                        if (ce != null)
                            this._currentClient.UserSettings.CustomEmoticons.Remove(ce);
                        this._currentClient.UserSettings.CustomEmoticons.Add(new ContactEmoticon() { Keyword = m.EmoticonShortcut, Extension = Path.GetExtension(m.EmoticonPath) });
                    }
                    break;
                case NotifyCollectionChangedAction.Reset:
                    this._currentClient.UserSettings.CustomEmoticons.Clear();
                    break;
                default:
                    break;
            }
        }
        private bool CanExecuteAddContact(IContact contact)
        {
            if (contact != null && _currentClient.FriendUsernames.Contains(contact.UserName, StringComparer.OrdinalIgnoreCase))
                return false;



            return true;
        }

        private void ManageGroupCommandAction(IChatGroup group)
        {
            Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, group, "OpenManageGroup"));
        }

        private void OpenGroupCommandAction(IChatGroup group)
        {
            ViewModelChat vm = null;
            if (!_currentClient.ClosedConversations.TryRemove(group.ID, out vm))
            {
                vm = new ViewModelGroupChat(_currentClient, group, this.CurrentUser, true, true) { IsPinned = _currentClient.UserSettings.OpenChats.Any(x => x.ID == group.ID && x.IsPinned == true) };
            }
            Messenger.Default.Send(new NotificationMessage<ViewModelChat>(this, vm, "OpenGroup"));
        }



        private void BlockGroupCommandAction(object obj)
        {
            IChatGroup iGroup = (IChatGroup)obj;

            switch (iGroup.Type)
            {
                case GroupType.JiveChatGroup:
                    {
                        JiveChatGroupModel group = (JiveChatGroupModel)obj;
                        if (group.IsBlocked)
                        {
                            _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.UnblockGroup) { RecipientID = group.ID });
                            _currentClient.UserSettings.BlockedGroups.Remove(group.ID);
                            group.IsBlocked = false;
                        }
                        else
                        {
                            _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.BlockGroup) { RecipientID = group.ID });
                            _currentClient.UserSettings.BlockedGroups.Add(group.ID);
                            group.IsBlocked = true;
                        }
                        this.ContactsViewSource.View.Refresh();
                        break;
                    }
                default:
                    break;

            }
        }

        private void LeaveGroupCommandAction(object obj)
        {
            IChatGroup group = (IChatGroup)obj;
            switch (group.Type)
            {
                case GroupType.JiveChatGroup:
                    _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.LeaveGroup) { RecipientID = group.ID });
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        private void IgnoreContactCommandAction(IContact contact)
        {
            if (contact.IsIgnored)
            {
                _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.UnignoreContact) { RecipientID = contact.UserName });
                _currentClient.UserSettings.IgnoredContacts.Remove(contact.UserName);
                contact.IsIgnored = false;
            }
            else
            {
                _currentClient.Connection.SendMessage(new ChatMessage(_currentClient.CurrentUserInfo.ID, _currentClient.CurrentUserInfo.UserName, MessageType.IgnoreContact) { RecipientID = contact.UserName });
                _currentClient.UserSettings.IgnoredContacts.Add(contact.UserName);
                contact.IsIgnored = true;
            }
        }

        private void OpenNotificationsCommandAction()
        {
            Messenger.Default.Send(new NotificationMessage(this, "OpenNotifications"));
        }

        private void OpenPmCommandAction(IContact contact)
        {
            if (contact == null)
                return;

            _currentClient.OpenNewPM(contact.UserID);
        }

        private bool CanExecuteAsGroupAdmin(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner || priv == GroupPrivilege.Moderator)
                                return true;
                        }
                        break;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteAsGroupOwner(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner)
                                return true;
                        }
                        break;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteLeaveGroup(object obj)
        {
            if (_currentClient.CurrentUserInfo == null)
                return false;
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        GroupPrivilege priv;
                        if (((JiveChatGroupModel)group).GroupMembers.TryGetValue(this._currentClient.CurrentUserInfo.UserName, out priv))
                        {
                            if (priv == GroupPrivilege.Owner)
                                return false;
                        }
                        return true;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }
        private bool CanExecuteBlockGroup(object obj)
        {
            if (obj != null && obj is IChatGroup)
            {
                IChatGroup group = (IChatGroup)obj;
                switch (group.Type)
                {
                    case GroupType.JiveChatGroup:
                        return true;
                    case GroupType.JiveFriendsGroup:
                        return false;
                    default:
                        throw new NotImplementedException();
                }
            }
            return false;
        }


        private void InviteContactToGroupCommandAction(object obj)
        {
            IChatGroup group = (IChatGroup)obj;
            Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, group, "InviteContactToChatGroup"));
        }

        private void OpenCreateGroupWindowCommandAction()
        {
            Messenger.Default.Send(new NotificationMessage(this, "OpenCreateGroup"));
        }

        private void AddContactCommandAction(IContact contact)
        {
            object target = contact != null ? contact.UserName : null;
            Messenger.Default.Send(new NotificationMessage(this, target, "OpenAddContacts"));
        }
        public void RunOnNetworkConnect()
        {
            CollectionViewSource src = new CollectionViewSource();
            src.Source = Contacts;
            //this adds the client status  groups - i.e. "Online" and "Offline".
            var onlineGroup = new JiveClientStatusGroupModel("JiveClientStatusGroupOnline", JiveStatusGroupStatus.Online);
            var offlineGroup = new JiveClientStatusGroupModel("JiveClientStatusGroupOffline", JiveStatusGroupStatus.Offline);
            JiveFriendsGroupModel friendsGroup = new JiveFriendsGroupModel("JiveFriendsGroup");
            JiveFriendsGroupModel offlineFriendsGroup = new JiveFriendsGroupModel("OfflineJiveFriendsGroup");

            //This will only run with a fresh database, i.e. on first time connect to the server. 
            //We open the online and offline member groups as a default value.
            if (_currentClient.UserSettings.ExpandedGroups == null)
            {
                _currentClient.UserSettings.ExpandedGroups = new List<string>();
                _currentClient.UserSettings.ExpandedGroups.Add(onlineGroup.ID);
                _currentClient.UserSettings.ExpandedGroups.Add(offlineGroup.ID);
                _currentClient.UserSettings.ExpandedGroups.Add(friendsGroup.ID);
            }

            onlineGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == onlineGroup.ID);
            offlineGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == offlineGroup.ID);
            friendsGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == friendsGroup.ID);
            offlineFriendsGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == offlineFriendsGroup.ID);
            var groupDescription = new PropertyGroupDescription("ClientStatus", new ContactGroupStatusConverter() { OfflineGroup = offlineGroup, OnlineGroup = onlineGroup });

            groupDescription.GroupNames.Add(onlineGroup);
            groupDescription.GroupNames.Add(offlineGroup);
            src.GroupDescriptions.Add(groupDescription);

            groupDescription = new PropertyGroupDescription("Instance", new ContactChatGroupConverter() { ContactModels = this.Contacts, CurrentClient = this.CurrentUser, FriendsGroup = friendsGroup, OfflineFriendsGroup = offlineFriendsGroup });

            // this foreach must at least add clusters that can't be
            // derived from items - i.e. groups with no items in them
            foreach (var grp in this.CurrentUser.ChatGroups)
                groupDescription.GroupNames.Add(grp);

            src.GroupDescriptions.Add(groupDescription);
            src.Filter += src_Filter;
            this.ContactsViewSource = src;
            this._isConnected = true;
        }

        public void RunOnNetworkDisconnect()
        {
            this.Cleanup();
        }
        void src_Filter(object sender, FilterEventArgs args)
        {
            CollectionViewSource src = (CollectionViewSource)sender;
            IContact model = (IContact)args.Item;

            if (model.UserID == CurrentUser.UserID || !model.ShowInContactList)
                args.Accepted = false;
            else
                args.Accepted = true;
        }

        public override void Cleanup()
        {
            //  base.Cleanup();

            Contacts = new FilteredObservableCollection<IContact>();

            App.Current.Dispatcher.Invoke(() =>
                {
                    if (ContactsViewSource != null)
                    {
                        ContactsViewSource.Filter -= src_Filter;
                        ContactsViewSource = null;
                    }
                });


            CurrentUser = null;
        }

        void Contacts_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (IContact newItem in e.NewItems)
                {

                    //Add listener for each item on PropertyChanged event
                    newItem.PropertyChanged += IContact_PropertyChanged;
                }
            }

            if (e.OldItems != null)
            {
                foreach (IContact oldItem in e.OldItems)
                {
                    oldItem.PropertyChanged -= IContact_PropertyChanged;
                }
            }
        }

        void IContact_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "ClientStatus":
                case "ChatGroups":
                case "ShowInContactList":
                    this.Contacts.RefreshFilter(sender as IContact);
                    break;
            }
        }


        public void AddOrUpdateContact(UserInfo user, bool processAvatar)
        {
            lock (LockObject)
            {
                bool isFriend = user.Equals(_currentClient.CurrentUserInfo) ? true : this._currentClient.FriendUsernames.Any(x => x == user.UserName);
                bool addContact = false;
                JiveContactModel contact = Contacts.FirstOrDefault(x => x.UserID == user.UserID) as JiveContactModel;
                if (contact == null)
                {
                    contact = new JiveContactModel(user.ID, user.UserName, user.PrettyName, SharedStaticMethods.GetDeviceType(user.UserID), isFriend)
                    {
                        FontWeight = StaticMethods.ConvertFromString<FontWeight>(user.FontWeight),
                        FontStyle = StaticMethods.ConvertFromString<FontStyle>(user.FontStyle),
                        FontFamily = StaticMethods.ConvertFromString<FontFamily>(user.FontFamily),
                        FontColor = new SolidColorBrush(StaticMethods.HexStringToColor(user.Color)),
                        FontSize = _currentClient.UserSettings.FontSize.GetValueOrDefault(12)
                    };
                    contact.ShowInContactList = true;

                    addContact = true;
                }
                else
                {
                    if (user.Color != null || contact.FontColor == null)
                        contact.FontColor = new SolidColorBrush(StaticMethods.HexStringToColor(user.Color));
                    if (!string.IsNullOrEmpty(user.FontWeight) || contact.FontWeight == null)
                        contact.FontWeight = StaticMethods.ConvertFromString<FontWeight>(user.FontWeight);
                    if (!string.IsNullOrEmpty(user.FontStyle) || contact.FontStyle == null)
                        contact.FontStyle = StaticMethods.ConvertFromString<FontStyle>(user.FontStyle);
                    //also add font family and font size from user settings.
                    if (_currentClient.UserSettings.FontSize.HasValue)
                        contact.FontSize = _currentClient.UserSettings.FontSize.Value;
                    if (_currentClient.UserSettings.FontFamily != null)
                        contact.FontFamily = StaticMethods.ConvertFromString<FontFamily>(_currentClient.UserSettings.FontFamily) ?? StaticMethods.ConvertFromString<FontFamily>("Segoe UI");
                }

                if (user.ClientStatus.HasValue)
                    contact.ClientStatus = user.ClientStatus.Value;

                if (user.NickName != null)
                {
                    contact.NickName = user.NickName;
                    Messenger.Default.Send(new NotificationMessage<IContact>(this, contact, "UpdatePrivateMessage"));
                }

                contact.IsFriend = isFriend;
                contact.IsIgnored = _currentClient.UserSettings.IgnoredContacts.Any(x => x == user.UserName);
                if (user.TimestampAfk.HasValue)
                    contact.AfkStart = user.TimestampAfk.Value.DateTime;
                if (user.TimestampOffline.HasValue)
                    contact.OfflineStart = user.TimestampOffline.Value.DateTime;

                bool emoticonsChanged = false;
                if (processAvatar)
                {
                    bool setDefaultAvatar = false;
                    if (user.Avatar != null && !String.IsNullOrEmpty(user.Avatar.Hash) && !String.IsNullOrEmpty(user.Avatar.Extension))
                    {
                        FileInfo fi = new FileInfo(StaticMethods.CombinePath(ProgramSettings.Instance.AvatarFolder, contact.UserName, user.Avatar.Hash + user.Avatar.Extension));
                        if (fi.Exists)
                            contact.Avatar = StaticMethods.BitmapFromUri(new Uri(fi.FullName));
                        else
                            setDefaultAvatar = true;
                    }
                    else
                        setDefaultAvatar = true;

                    if (setDefaultAvatar)
                        contact.Avatar = new BitmapImage(new Uri("/JiveMessenger;component/Resources/Images/avatar_empty.png", UriKind.Relative));
                }

                var newChatGroups = new ObservableCollection<JiveChatGroupModel>(user.ChatGroups.Select(x => new JiveChatGroupModel(x.ID, x.Name, x.GroupMembers)));// ?? new ChatGroup[] { };

                if (user.Equals(_currentClient.CurrentUserInfo))
                {

                    //This is our own user, so we update our chat groups.
                    CurrentUser = contact;

                    foreach (JiveChatGroupModel group in newChatGroups)
                    {
                        group.IsExpanded = _currentClient.UserSettings.ExpandedGroups == null || _currentClient.UserSettings.ExpandedGroups.Any(x => x == group.ID);
                        if (!contact.ChatGroups.Contains(group))
                            contact.ChatGroups.Add(group);

                    }
                    foreach (IChatGroup group in new List<IChatGroup>(contact.ChatGroups))
                    {
                        switch (group.Type)
                        {
                            case GroupType.JiveChatGroup:
                                ((JiveChatGroupModel)group).IsBlocked = _currentClient.UserSettings.BlockedGroups.Any(x => x == group.ID);
                                if (!newChatGroups.Contains(group))
                                {
                                    contact.ChatGroups.Remove(group);
                                    Messenger.Default.Send(new NotificationMessage<string>(this, group.ID, "CloseTab"));
                                }
                                break;
                            default:
                                break;
                        }
                    }

                    if (this.ContactsViewSource != null && !Array.Equals(contact.ChatGroups, newChatGroups))
                    {
                        //update permanent chat groups in viewsource
                        CollectionViewSource src = this.ContactsViewSource;

                        using (src.DeferRefresh())
                        {
                            PropertyGroupDescription desc = src.GroupDescriptions.FirstOrDefault(x => ((PropertyGroupDescription)x).PropertyName == "Instance") as PropertyGroupDescription;
                            if (desc != null && newChatGroups != null)
                            {
                                src.GroupDescriptions.Remove(desc);
                                JiveFriendsGroupModel friendsGroup = new JiveFriendsGroupModel("JiveFriendsGroup");
                                JiveFriendsGroupModel offlineFriendsGroup = new JiveFriendsGroupModel("OfflineJiveFriendsGroup");
                                friendsGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == friendsGroup.ID);
                                offlineFriendsGroup.IsExpanded = _currentClient.UserSettings.ExpandedGroups.Any(x => x == offlineFriendsGroup.ID);
                                var groupDescription = new PropertyGroupDescription("Instance", new ContactChatGroupConverter() { ContactModels = Contacts, CurrentClient = this.CurrentUser, FriendsGroup = friendsGroup, OfflineFriendsGroup = offlineFriendsGroup });

                                foreach (var grp in newChatGroups)
                                {
                                    groupDescription.GroupNames.Add(grp);
                                    Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, grp, "UpdateGroup"));
                                }
                                src.GroupDescriptions.Add(groupDescription);

                            }
                        }
                    }
                }
                else
                {
                    contact.ChatGroups = new ObservableCollection<IChatGroup>(newChatGroups);
                }


                if (user.CustomEmoticons != null)
                {
                    var newEmoticonList = CreateCustomEmoticonList(contact, user.CustomEmoticons);
                    if (contact.CustomEmoticons == null || !contact.CustomEmoticons.SequenceEqual(newEmoticonList))
                    {
                        emoticonsChanged = true;
                        contact.CustomEmoticons = newEmoticonList;
                    }
                }

                //Iterate through the chat groups that the contact owns and we are a member of, and update changes.
                foreach (JiveChatGroupModel group in newChatGroups.Where(group => this.CurrentUser.ChatGroups.Any(x => x.ID == group.ID)))
                {
                    JiveChatGroupModel grp = (JiveChatGroupModel)this.CurrentUser.ChatGroups.FirstOrDefault(x => x.Type == GroupType.JiveChatGroup && x.ID == group.ID);

                    grp.GroupMembers = group.GroupMembers;
                    if (group.GroupMembers.Any(x => x.Key == user.UserName && x.Value == GroupPrivilege.Owner))
                    {
                        grp.Name = group.Name;
                        Messenger.Default.Send(new NotificationMessage<IChatGroup>(this, grp, "UpdateGroup"));
                    }

                }

                //Show only one contact for clients connected on multiple devices, prioritizing online and present clients before afk or busy, and pc clients before mobile.
                Messenger.Default.Send(new NotificationMessage<IContact>(this, contact, "UpdateConversationLineTransformers"));
                Messenger.Default.Send(new NotificationMessage(this, "RedrawChatWindow"));
                if (emoticonsChanged)
                    Messenger.Default.Send(new NotificationMessage(VmHelper.Locator.ViewModelMainWindow.CurrentActiveViewModel, "ScrollDocument"));


                if (addContact)
                {
                    Contacts.Add(contact);
                    Logger.Debug("Added contact: " + contact.UserID);
                }

                var onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName && x.DeviceType == DeviceType.PC && x.ClientStatus != ClientStatus.Offline);
                if (onlineContact == null)
                    onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName && x.ClientStatus != ClientStatus.Offline);
                if (onlineContact == null)
                    onlineContact = this.Contacts.FirstOrDefault(x => x.UserName == user.UserName);
                if (onlineContact != null)
                {
                    onlineContact.ShowInContactList = true;

                    foreach (IContact cont in this.Contacts.Where(x => x.UserName == onlineContact.UserName && x.UserID != onlineContact.UserID))
                    {
                        cont.ShowInContactList = false;
                    }
                }

                if (this.ContactsViewSource != null)
                    this.ContactsViewSource.View.Refresh();
            }
        }


        private ObservableCollection<JiveCustomEmoticonModel> CreateCustomEmoticonList(IContact user, IEnumerable<ContactEmoticon> customEmoticons)
        {
            ObservableCollection<JiveCustomEmoticonModel> list = new ObservableCollection<JiveCustomEmoticonModel>();
            foreach (ContactEmoticon ce in customEmoticons.OrderBy(x => x.Index))
            {
                string folder = user.Equals(this.CurrentUser) ? ProgramSettings.Instance.CustomEmoticonFolder : ProgramSettings.Instance.CustomContactEmoticonFolder;
                string filePath = StaticMethods.CombinePath(folder, user.UserName, ce.Hash + ce.Extension);
                list.Add(new JiveCustomEmoticonModel() { EmoticonShortcut = ce.Keyword, EmoticonPath = filePath });
            }
            return list;
        }
        public void SetContactAsOffline(string userID)
        {
            IContact offlineContact = Contacts.FirstOrDefault(x => x.UserID == userID);
            if (offlineContact != null)
            {
               
                //if (!_currentClient.ContactClients.Values.Any(x => x.UserID == userID))
                //{
                    offlineContact.ClientStatus = ClientStatus.Offline;
                    if (offlineContact is JiveContactModel)
                    {
                        ((JiveContactModel)offlineContact).OfflineStart = DateTime.Now;
                    }
                    IContact newOnlineContact = Contacts.FirstOrDefault(x => x.UserName == offlineContact.UserName && x.ClientStatus != ClientStatus.Offline);
                    if (newOnlineContact != null)
                    {
                        newOnlineContact.ShowInContactList = true;
                        offlineContact.ShowInContactList = false;
                    }
                    this.ContactsViewSource.View.Refresh();
                    Logger.Debug("Marked contact as offline: " + offlineContact.UserName);
                //}
            }
        }
    }
}